/*
 * Copyright (C) 2017 zhouyou(478319399@qq.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.shuyihui.rxhttp.request

import com.shuyihui.rxhttp.body.ProgressResponseCallBack
import com.shuyihui.rxhttp.body.RequestBodyUtils.create
import com.shuyihui.rxhttp.body.UploadProgressRequestBody
import com.shuyihui.rxhttp.model.HttpParams
import com.shuyihui.rxhttp.utils.Utils
import io.reactivex.Observable
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.ResponseBody
import retrofit2.http.Body
import java.io.File
import java.io.InputStream
import java.util.*

/**
 *
 * 描述：body请求的基类
 */
abstract class BaseBodyRequest<out R : BaseBodyRequest<R>>(url: String?) : BaseRequest<R>(url) {
    @JvmField
    protected var string //上传的文本内容
            : String? = null
    @JvmField
    protected var mediaType //上传的文本内容
            : MediaType? = null
    @JvmField
    protected var json //上传的Json
            : String? = null
    protected var bs //上传的字节数据
            : ByteArray? = null

    @JvmField
    protected var `object` //上传的对象
            : Any? = null
    protected var requestBody //自定义的请求体
            : RequestBody? = null

    enum class UploadType {
        /**
         * MultipartBody.Part方式上传
         */
        PART,

        /**
         * Map RequestBody方式上传
         */
        BODY
    }

    private var currentUploadType = UploadType.PART
    fun requestBody(requestBody: RequestBody?): R {
        this.requestBody = requestBody
        return this as R
    }

    /**
     * 注意使用该方法上传字符串会清空实体中其他所有的参数，头信息不清除
     */
    fun upString(string: String?): R {
        this.string = string
        mediaType = "text/plain".toMediaTypeOrNull()
        return this as R
    }

    fun upString(string: String?, mediaType: String): R {
        this.string = string
        Utils.checkNotNull(mediaType, "mediaType==null")
        this.mediaType = mediaType.toMediaTypeOrNull()
        return this as R
    }

    fun upObject(@Body `object`: Any?): R {
        this.`object` = `object`
        return this as R
    }

    /**
     * 注意使用该方法上传字符串会清空实体中其他所有的参数，头信息不清除
     */
    fun upJson(json: String?): R {
        this.json = json
        return this as R
    }

    /**
     * 注意使用该方法上传字符串会清空实体中其他所有的参数，头信息不清除
     */
    fun upBytes(bs: ByteArray?): R {
        this.bs = bs
        return this as R
    }

    fun params(key: String?, file: File, responseCallBack: ProgressResponseCallBack?): R {
        params.put<File>(key = key, file = file, responseCallBack = responseCallBack)
        return this as R
    }

    fun params(
        key: String?,
        stream: InputStream,
        fileName: String,
        responseCallBack: ProgressResponseCallBack?
    ): R {
        params.put(key, stream, fileName, responseCallBack)
        return this as R
    }

    fun params(
        key: String?,
        bytes: ByteArray,
        fileName: String,
        responseCallBack: ProgressResponseCallBack?
    ): R {
        params.put(key, bytes, fileName, responseCallBack)
        return this as R
    }

    fun addFileParams(
        key: String?,
        files: List<File>?,
        responseCallBack: ProgressResponseCallBack?
    ): R {
        params.putFileParams(key, files, responseCallBack)
        return this as R
    }

    fun addFileWrapperParams(key: String?, fileWrappers: List<HttpParams.FileWrapper<*>?>?): R {
        params.putFileWrapperParams(key, fileWrappers)
        return this as R
    }

    fun params(
        key: String?,
        file: File,
        fileName: String,
        responseCallBack: ProgressResponseCallBack?
    ): R {
        params.put(key, file, fileName, responseCallBack)
        return this as R
    }

    fun <T> params(
        key: String?,
        file: T,
        fileName: String,
        contentType: MediaType?,
        responseCallBack: ProgressResponseCallBack?
    ): R {
        params.put(key, file, fileName, contentType, responseCallBack)
        return this as R
    }

    /**
     * 上传文件的方式，默认part方式上传
     */
    fun <T> uploadType(uploadtype: UploadType): R {
        currentUploadType = uploadtype
        return this as R
    }

    override fun generateRequest(): Observable<ResponseBody?> {
        if (requestBody != null) { //自定义的请求体
            return apiManager!!.postBody(url!!, requestBody!!)
        } else if (json != null) { //上传的Json
            val body = RequestBody.create(
                "application/json; charset=utf-8".toMediaTypeOrNull(),
                json!!
            )
            return apiManager!!.postJson(url!!, body)
        } else if (`object` != null) { //自定义的请求object
            return apiManager!!.postBody(url!!, `object`!!)
        } else if (string != null) { //上传的文本内容
            val body = RequestBody.create(mediaType, string!!)
            return apiManager!!.postBody(url!!, body)
        } else if (bs != null) { //上传的字节数据
            val body = RequestBody.create("application/octet-stream".toMediaTypeOrNull(), bs!!)
            return apiManager!!.postBody(url!!, body)
        }
        return if (params.fileParamsMap?.isEmpty() == true) {
            apiManager!!.post(url!!, params.urlParamsMap)
        } else {
            if (currentUploadType == UploadType.PART) { //part方式上传
                uploadFilesWithParts()
            } else { //body方式上传
                uploadFilesWithBodys()
            }
        }
    }

    protected fun uploadFilesWithParts(): Observable<ResponseBody?> {
        val parts: MutableList<MultipartBody.Part> = ArrayList<MultipartBody.Part>()
        //拼接参数键值对
        for ((key, value) in params.urlParamsMap!!) {
            value?.let { MultipartBody.Part.createFormData(key!!, it) }?.let { parts.add(it) }
        }
        //拼接文件
        for ((key, fileValues) in params.fileParamsMap!!) {
            for (fileWrapper in fileValues) {
                addFile(key, fileWrapper)?.let { parts.add(it) }
            }
        }
        return apiManager!!.uploadFiles(url!!, parts)
    }

    protected fun uploadFilesWithBodys(): Observable<ResponseBody?> {
        val mBodyMap: MutableMap<String, RequestBody> = HashMap()
        //拼接参数键值对
        params.urlParamsMap?.apply {

            for ((key, value) in this) {
                val body = RequestBody.create("text/plain".toMediaTypeOrNull(), value!!)
                mBodyMap[key!!] = body
            }
            //拼接文件
            for ((key, fileValues) in params.fileParamsMap!!.entries) {
                for (fileWrapper in fileValues!!) {
                    val requestBody = getRequestBody(fileWrapper)
//                    val uploadProgressRequestBody =
                        fileWrapper.responseCallBack?.let {
                            UploadProgressRequestBody(requestBody,
                                it
                            )
                        }?.let {
                            mBodyMap[key!!] = it
                        }
                }
            }
        }
        return apiManager!!.uploadFiles(url!!, mBodyMap)
    }

    //文件方式
    private fun addFile(key: String, fileWrapper: HttpParams.FileWrapper<*>): MultipartBody.Part? {
        //MediaType.parse("application/octet-stream", file)
        val requestBody = getRequestBody(fileWrapper)
        Utils.checkNotNull(
            requestBody,
            "requestBody==null fileWrapper.file must is File/InputStream/byte[]"
        )
        //包装RequestBody，在其内部实现上传进度监听
        return if (fileWrapper.responseCallBack != null) {
            val uploadProgressRequestBody = UploadProgressRequestBody(
                requestBody,
                fileWrapper.responseCallBack!!
            )
            MultipartBody.Part.createFormData(
                key,
                fileWrapper.fileName,
                uploadProgressRequestBody
            )
        } else {
            requestBody?.let { MultipartBody.Part.createFormData(key, fileWrapper.fileName, it) }
        }
    }

    private fun getRequestBody(fileWrapper: HttpParams.FileWrapper<*>): RequestBody ?{
        var requestBody: RequestBody? = null
        if (fileWrapper.file is File) {
            requestBody = RequestBody.create(fileWrapper.contentType, (fileWrapper.file as File))
        } else if (fileWrapper.file is InputStream) {
            //requestBody = RequestBodyUtils.create(RequestBodyUtils.MEDIA_TYPE_MARKDOWN, (InputStream) fileWrapper.file);
            requestBody = create(fileWrapper.contentType, (fileWrapper.file as InputStream))
        } else if (fileWrapper.file is ByteArray) {
            requestBody =
                RequestBody.create(fileWrapper.contentType, (fileWrapper.file as ByteArray))
        }
        return requestBody
    }
}